import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository, DataSource } from "typeorm";
import {
  HouseTrainingTaskEntity,
  HouseTrainingType,
} from "../entities/house-training-task.entity";
import { CharacterResourcesEntity } from "../entities/character-resources.entity";
import { BasicItemEntity } from "../entities/basic-item.entity";
import { InventoryService } from "./inventory.service";
import { CharacterResidenceEntity } from "../entities/character-residence.entity";
import { CharacterAttributeEntity } from "../entities/character-attribute.entity";
import { DeputyGeneral } from "../entities/deputy-general.entity";
import { PlayerDeputy } from "../entities/player-deputy.entity";
import { CharacterHealClaimEntity } from "../entities/character-heal-claim.entity";
import { DeputyAttributeService } from "./deputy-attribute.service";

@Injectable()
export class HouseTrainingService {
  constructor(
    @InjectRepository(HouseTrainingTaskEntity)
    private readonly taskRepo: Repository<HouseTrainingTaskEntity>,
    @InjectRepository(CharacterResourcesEntity)
    private readonly resourcesRepo: Repository<CharacterResourcesEntity>,
    @InjectRepository(CharacterResidenceEntity)
    private readonly residenceRepo: Repository<CharacterResidenceEntity>,
    @InjectRepository(BasicItemEntity)
    private readonly basicItemRepo: Repository<BasicItemEntity>,
    private readonly dataSource: DataSource,
    private readonly inventoryService: InventoryService,
    private readonly deputyAttributeService: DeputyAttributeService
  ) {}

  private libraryLevelToItemId(level: number): number {
    const map: Record<number, number> = {
      1: 131,
      2: 132,
      3: 133,
      4: 134,
      5: 135,
    };
    return map[level] || 0;
  }

  private gongfangLevelToItemId(levelIndex: number): number {
    // 1-初级 2-高级 3-特级 4-密宗
    const map: Record<number, number> = {
      1: 147,
      2: 148,
      3: 149,
      4: 425,
    };
    return map[levelIndex] || 0;
  }

  private libraryLevelToGold(level: number): number {
    // 以基础表为准，如不存在则按常规表 20/40/60/80/100
    const fallback: Record<number, number> = {
      1: 20,
      2: 40,
      3: 60,
      4: 80,
      5: 100,
    };
    return fallback[level] || 0;
  }

  /** 书房训练（按数量）：每本按固定分钟数计算总时长，费用=单价×数量 */
  async startLibraryTrainingByCount(params: {
    characterId: number;
    level: number; // 1-5
    count: number; // 本数
    minutesPerBook?: number; // 每本分钟，默认60
  }): Promise<{ act_id: number; code: number; data?: any; msg?: string }> {
    const { characterId, level, count } = params;
    const minutesPerBook = Math.max(1, Math.floor(params.minutesPerBook ?? 60));
    if (level < 1 || level > 5)
      return { act_id: 1515, code: 8, msg: "技能书等级非法" };
    if (!Number.isFinite(count) || count <= 0)
      return { act_id: 1515, code: 8, msg: "数量不正确" };

    const itemId = this.libraryLevelToItemId(level);
    const unitGold = this.libraryLevelToGold(level);
    const totalGold = unitGold * count;
    const duration = minutesPerBook * count;

    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();
    try {
      const res = await queryRunner.manager.findOne(CharacterResourcesEntity, {
        where: { characterId },
      });
      if (!res || res.gold < totalGold) {
        await queryRunner.rollbackTransaction();
        return { act_id: 1515, code: 8, msg: "金砖不足或资源未初始化" };
      }

      await queryRunner.manager.update(
        CharacterResourcesEntity,
        { characterId },
        { gold: res.gold - totalGold, updated_at: new Date() }
      );

      const startAt = new Date();
      const endAt = new Date(startAt.getTime() + duration * 60 * 1000);
      const task = queryRunner.manager.create(HouseTrainingTaskEntity, {
        characterId,
        type: HouseTrainingType.Library,
        level,
        count,
        itemId,
        durationMinutes: duration,
        startAt,
        endAt,
        costGold: totalGold,
        status: 1,
        remark: "",
        created_at: new Date(),
        updated_at: new Date(),
      });
      await queryRunner.manager.save(task);

      await queryRunner.commitTransaction();
      return { act_id: 1515, code: 0, data: "开始修炼" };
    } catch (e: any) {
      await queryRunner.rollbackTransaction();
      return { act_id: 1515, code: -1, msg: e?.message || "开始训练失败" };
    } finally {
      await queryRunner.release();
    }
  }

  async startLibraryTraining(params: {
    characterId: number;
    level: number; // 1-5
    duration: number; // 30/60/90/120/150
  }): Promise<{ act_id: number; code: number; data?: any; msg?: string }> {
    const { characterId, level, duration } = params;
    if (level < 1 || level > 5) {
      return { act_id: 1515, code: 8, msg: "技能书等级非法" };
    }
    if (![30, 60, 90, 120, 150].includes(duration)) {
      return { act_id: 1515, code: 8, msg: "训练时长不支持" };
    }

    const itemId = this.libraryLevelToItemId(level);
    const costGold = this.libraryLevelToGold(level);

    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();
    try {
      const res = await queryRunner.manager.findOne(CharacterResourcesEntity, {
        where: { characterId },
      });
      if (!res || res.gold < costGold) {
        await queryRunner.rollbackTransaction();
        return { act_id: 1515, code: 8, msg: "金砖不足或资源未初始化" };
      }

      await queryRunner.manager.update(
        CharacterResourcesEntity,
        { characterId },
        { gold: res.gold - costGold, updated_at: new Date() }
      );

      const startAt = new Date();
      const endAt = new Date(startAt.getTime() + duration * 60 * 1000);
      const task = queryRunner.manager.create(HouseTrainingTaskEntity, {
        characterId,
        type: HouseTrainingType.Library,
        level,
        count: 1,
        itemId,
        durationMinutes: duration,
        startAt,
        endAt,
        costGold,
        status: 1,
        remark: "",
        created_at: new Date(),
        updated_at: new Date(),
      });
      await queryRunner.manager.save(task);

      await queryRunner.commitTransaction();
      return { act_id: 1515, code: 0, data: "开始修炼" };
    } catch (e: any) {
      await queryRunner.rollbackTransaction();
      return { act_id: 1515, code: -1, msg: e?.message || "开始训练失败" };
    } finally {
      await queryRunner.release();
    }
  }

  async startAlchemyTraining(params: {
    characterId: number;
    itemId: number; // 421 魂元丹
    duration: number; // 固定60
  }): Promise<{ act_id: number; code: number; data?: any; msg?: string }> {
    const { characterId, itemId, duration } = params;
    if (itemId !== 421 || duration !== 60) {
      return { act_id: 6406, code: 8, msg: "参数不支持" };
    }

    // 以基础表 goldPrice 为准，fallback 80
    const basic = await this.basicItemRepo.findOne({
      where: { id: itemId, status: 1 },
    });
    const costGold = basic?.goldPrice ?? 80;

    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();
    try {
      const res = await queryRunner.manager.findOne(CharacterResourcesEntity, {
        where: { characterId },
      });
      if (!res || res.gold < costGold) {
        await queryRunner.rollbackTransaction();
        return { act_id: 6406, code: 8, msg: "金砖不足或资源未初始化" };
      }

      await queryRunner.manager.update(
        CharacterResourcesEntity,
        { characterId },
        { gold: res.gold - costGold, updated_at: new Date() }
      );

      const startAt = new Date();
      const endAt = new Date(startAt.getTime() + duration * 60 * 1000);
      const task = queryRunner.manager.create(HouseTrainingTaskEntity, {
        characterId,
        type: HouseTrainingType.Alchemy,
        level: 0,
        count: 1,
        itemId,
        durationMinutes: duration,
        startAt,
        endAt,
        costGold,
        status: 1,
        remark: "",
        created_at: new Date(),
        updated_at: new Date(),
      });
      await queryRunner.manager.save(task);

      await queryRunner.commitTransaction();
      return { act_id: 6406, code: 0, data: "开始修炼" };
    } catch (e: any) {
      await queryRunner.rollbackTransaction();
      return { act_id: 6406, code: -1, msg: e?.message || "开始炼丹失败" };
    } finally {
      await queryRunner.release();
    }
  }

  /** 炼丹（按数量）：固定每个60分钟、按基础表单价计费（默认80） */
  async startAlchemyTrainingByCount(params: {
    characterId: number;
    count: number;
    minutesPerItem?: number; // 默认60
    itemId?: number; // 默认421
  }): Promise<{ act_id: number; code: number; data?: any; msg?: string }> {
    const { characterId } = params;
    const count = Math.floor(Number(params.count || 0));
    const minutesPerItem = Math.max(1, Math.floor(params.minutesPerItem ?? 60));
    const itemId = Number(params.itemId ?? 421);
    if (!Number.isFinite(count) || count <= 0) {
      return { act_id: 6406, code: 8, msg: "数量不正确" };
    }

    const basic = await this.basicItemRepo.findOne({
      where: { id: itemId, status: 1 },
    });
    const unitGold = basic?.goldPrice ?? 80;
    const totalGold = unitGold * count;
    const duration = minutesPerItem * count;

    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();
    try {
      const res = await queryRunner.manager.findOne(CharacterResourcesEntity, {
        where: { characterId },
      });
      if (!res || res.gold < totalGold) {
        await queryRunner.rollbackTransaction();
        return { act_id: 6406, code: 8, msg: "金砖不足或资源未初始化" };
      }

      await queryRunner.manager.update(
        CharacterResourcesEntity,
        { characterId },
        { gold: res.gold - totalGold, updated_at: new Date() }
      );

      const startAt = new Date();
      const endAt = new Date(startAt.getTime() + duration * 60 * 1000);
      const task = queryRunner.manager.create(HouseTrainingTaskEntity, {
        characterId,
        type: HouseTrainingType.Alchemy,
        level: 0,
        count,
        itemId,
        durationMinutes: duration,
        startAt,
        endAt,
        costGold: totalGold,
        status: 1,
        remark: "",
        created_at: new Date(),
        updated_at: new Date(),
      });
      await queryRunner.manager.save(task);

      await queryRunner.commitTransaction();
      return { act_id: 6406, code: 0, data: "开始修炼" };
    } catch (e: any) {
      await queryRunner.rollbackTransaction();
      return { act_id: 6406, code: -1, msg: e?.message || "开始炼丹失败" };
    } finally {
      await queryRunner.release();
    }
  }

  /** 功房（副将心法）按数量：基于房屋等级决定每本分钟与单价 */
  async startGongfangTrainingByCount(params: {
    characterId: number;
    levelIndex: number; // 1-初级,2-高级,3-特级,4-密宗（与UI radio_Index一致）
    count: number;
  }): Promise<{ act_id: number; code: number; data?: any; msg?: string }> {
    const { characterId } = params;
    const levelIndex = Math.floor(Number(params.levelIndex || 0));
    const count = Math.floor(Number(params.count || 0));
    if (![1, 2, 3, 4].includes(levelIndex)) {
      return { act_id: 1520, code: 8, msg: "心法等级参数不正确" };
    }
    if (!Number.isFinite(count) || count <= 0) {
      return { act_id: 1520, code: 8, msg: "数量不正确" };
    }

    // 查询房屋等级
    const residence = await this.residenceRepo.findOne({
      where: { characterId },
    });
    const houseLevel = residence?.houseLevel || 0;
    if (houseLevel < 3) {
      return { act_id: 1520, code: 8, msg: "功房未解锁(需3级房)" };
    }

    // 计算每本分钟与单价
    let minutesPer = 0;
    if (houseLevel === 3) minutesPer = 60;
    else if (houseLevel === 4) minutesPer = 40;
    else minutesPer = 20; // 5级及以上

    let unitGold = 0;
    if (houseLevel === 3) {
      if (levelIndex !== 1)
        return { act_id: 1520, code: 8, msg: "当前房屋等级不支持该心法" };
      unitGold = 30;
    } else if (houseLevel === 4) {
      if (![1, 2].includes(levelIndex))
        return { act_id: 1520, code: 8, msg: "当前房屋等级不支持该心法" };
      unitGold = levelIndex === 1 ? 30 : 40;
    } else {
      // 5级
      unitGold =
        levelIndex === 1
          ? 30
          : levelIndex === 2
          ? 40
          : levelIndex === 3
          ? 50
          : 60;
    }

    const totalGold = unitGold * count;
    const duration = minutesPer * count;

    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();
    try {
      const res = await queryRunner.manager.findOne(CharacterResourcesEntity, {
        where: { characterId },
      });
      if (!res || res.gold < totalGold) {
        await queryRunner.rollbackTransaction();
        return { act_id: 1520, code: 8, msg: "金砖不足或资源未初始化" };
      }

      await queryRunner.manager.update(
        CharacterResourcesEntity,
        { characterId },
        { gold: (res.gold || 0) - totalGold, updated_at: new Date() }
      );

      const startAt = new Date();
      const endAt = new Date(startAt.getTime() + duration * 60 * 1000);
      const task = queryRunner.manager.create(HouseTrainingTaskEntity, {
        characterId,
        type: HouseTrainingType.Gongfang,
        level: levelIndex,
        count,
        itemId: 0,
        durationMinutes: duration,
        startAt,
        endAt,
        costGold: totalGold,
        status: 1,
        remark: "",
        created_at: new Date(),
        updated_at: new Date(),
      });
      await queryRunner.manager.save(task);

      await queryRunner.commitTransaction();
      return { act_id: 1520, code: 0, data: "开始修炼" };
    } catch (e: any) {
      await queryRunner.rollbackTransaction();
      return { act_id: 1520, code: -1, msg: e?.message || "开始修炼失败" };
    } finally {
      await queryRunner.release();
    }
  }

  async settleDueTasks(
    push: (characterId: number, payload: any) => void
  ): Promise<number> {
    const now = new Date();
    const due = await this.taskRepo.find({ where: { status: 1 } });
    let processed = 0;
    for (const t of due) {
      if (!t.endAt || now < t.endAt) continue;
      try {
        // 根据训练类型确定产出物品
        const outItemId =
          t.type === HouseTrainingType.Alchemy
            ? t.itemId || 421
            : t.type === HouseTrainingType.Gongfang
            ? this.gongfangLevelToItemId(t.level)
            : this.libraryLevelToItemId(t.level);

        await this.inventoryService.addItemToInventory(
          t.characterId,
          outItemId,
          t.count
        );
        await this.taskRepo.update(
          { id: t.id },
          { status: 2, updated_at: new Date() }
        );
        // 完成通知：书房=1516，炼丹=6407，功房暂复用1516（前端实践房监听）
        const act_id = t.type === HouseTrainingType.Alchemy ? 6407 : 1516;
        push(t.characterId, {
          act_id,
          code: 0,
          data: {
            level: t.level,
            itemId: outItemId,
            count: t.count,
          },
        });
        processed++;
      } catch (e: any) {
        await this.taskRepo.update(
          { id: t.id },
          {
            status: 3,
            remark: e?.message || "发放失败",
            updated_at: new Date(),
          }
        );
        const act_id = t.type === HouseTrainingType.Library ? 1516 : 6407;
        push(t.characterId, {
          act_id,
          code: -1,
          msg: "物品发放失败，请稍后在背包查看",
        });
      }
    }
    return processed;
  }

  /** 查询书房训练状态：返回 -1（可设置）或剩余分钟数 */
  async getLibraryStatus(characterId: number): Promise<number> {
    const task = await this.taskRepo.findOne({
      where: { characterId, type: HouseTrainingType.Library, status: 1 },
    });
    if (!task || !task.endAt) return -1;
    const now = Date.now();
    const remainMs = task.endAt.getTime() - now;
    if (remainMs <= 0) return 0;
    return Math.ceil(remainMs / 60000);
  }

  async getGongfangStatus(
    characterId: number,
    getName: (cid: number) => Promise<string>
  ): Promise<{ busy: boolean; data: any }> {
    // 功房占用按角色隔离：只看当前角色的任务
    const task = await this.taskRepo.findOne({
      where: { type: HouseTrainingType.Gongfang, status: 1, characterId },
    });
    if (!task || !task.endAt) return { busy: false, data: null };
    const remain = Math.ceil((task.endAt.getTime() - Date.now()) / 60000);
    const n = await getName(task.characterId);
    return {
      busy: true,
      data: { c: task.characterId, n, t: Math.max(0, remain) },
    };
  }

  /**
   * 1483：房屋疗养（根据房屋等级每天 1-5 次）
   * - 将主角与“背包内所有副将”（pos=0）恢复到满状态（hp=maxHp，mp=maxMp；副将按属性服务计算）
   */
  async healAllByHouse(
    characterId: number
  ): Promise<{ act_id: number; code: number; msg?: string; data?: any }> {
    // 查询房屋等级
    const resi = await this.residenceRepo.findOne({ where: { characterId } });
    const houseLevel = resi?.houseLevel || 0;
    if (houseLevel <= 0) return { act_id: 1483, code: 8, msg: "您还没有房子" };

    const today = new Date();
    const yyyy = today.getFullYear();
    const mm = String(today.getMonth() + 1).padStart(2, "0");
    const dd = String(today.getDate()).padStart(2, "0");
    const dateStr = `${yyyy}-${mm}-${dd}`;

    // 当日最大次数 = houseLevel（1-5）
    const maxTimes = Math.max(1, Math.min(5, houseLevel));

    // 读取/创建当日计数
    let claim = await this.dataSource
      .getRepository(CharacterHealClaimEntity)
      .findOne({
        where: { characterId, claimDate: dateStr },
      });
    if (!claim) {
      claim = this.dataSource.getRepository(CharacterHealClaimEntity).create({
        characterId,
        claimDate: dateStr,
        usedCount: 0,
        created_at: new Date(),
        updated_at: new Date(),
      });
      await this.dataSource.getRepository(CharacterHealClaimEntity).save(claim);
    }
    if (claim.usedCount >= maxTimes)
      return { act_id: 1483, code: 8, msg: "当日次数已用完或条件不满足" };

    // 事务：恢复主角与所有副将
    await this.dataSource.transaction(async (manager) => {
      // 主角恢复至满
      const attr = await manager.findOne(CharacterAttributeEntity, {
        where: { characterId },
      });
      if (attr) {
        await manager.update(
          CharacterAttributeEntity,
          { characterId },
          { hp: attr.maxHp, mp: attr.maxMp, updated_at: new Date() }
        );
      }

      // 全部副将（含上阵与背包）恢复至满
      const owned = await manager.find(PlayerDeputy, {
        where: { player_id: characterId },
      });
      for (const pd of owned) {
        const d = await manager.findOne(DeputyGeneral, {
          where: { id: pd.deputy_id },
        });
        if (!d) continue;
        // 计算最大属性：优先使用副将属性服务计算
        let maxHp = Math.max(1, d.hp);
        let maxMp = Math.max(1, d.mp);
        try {
          const calc = await this.deputyAttributeService.calculate(d.id);
          if (typeof (calc as any).hp === "number")
            maxHp = Math.max(1, Number((calc as any).hp));
          if (typeof (calc as any).mp === "number")
            maxMp = Math.max(1, Number((calc as any).mp));
        } catch {}
        await manager.update(
          DeputyGeneral,
          { id: d.id },
          { hp: maxHp, mp: maxMp, updated_at: new Date() }
        );
      }

      // 增加当日已用次数
      await manager.update(
        CharacterHealClaimEntity,
        { id: claim!.id },
        { usedCount: claim!.usedCount + 1, updated_at: new Date() }
      );
    });

    return { act_id: 1483, code: 0, data: "全员已恢复满状态" };
  }

  async getAlchemyStatus(
    characterId: number,
    getName: (cid: number) => Promise<string>
  ): Promise<{ busy: boolean; data: any }> {
    // 炼丹房占用按角色隔离：只看当前角色的任务
    const task = await this.taskRepo.findOne({
      where: { type: HouseTrainingType.Alchemy, status: 1, characterId },
    });
    if (!task || !task.endAt) return { busy: false, data: null };
    const remain = Math.ceil((task.endAt.getTime() - Date.now()) / 60000);
    const n = await getName(task.characterId);
    return { busy: true, data: { n, t: Math.max(0, remain) } };
  }
}
